﻿#region License
/***********************************************
 *  WCFReactiveX v.1
 *  Written by James Levingston
 *  
 *  To get latest version or to see latest info:
 *  http://wcfreactivex.codeplex.com/
 *  
 *  The MIT License (MIT)
 *  http://en.wikipedia.org/wiki/MIT_License
 *  Copyright (C) <2011> by <James Levingston>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.

 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 ***/
#endregion

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace WCFReactiveX
{
    /// <summary>
    /// The Provider class is an observable container that functionalizes WCF Client requests and responses
    /// </summary>
    /// <typeparam name="TContext"></typeparam>
    /// <typeparam name="TToken"></typeparam>
    /// <typeparam name="TMessage"></typeparam>
    /// <typeparam name="TException"></typeparam>
    /// <typeparam name="TClient"></typeparam>
     public class Provider<TContext, TToken, TMessage, TException, TClient>: Provider<TContext, TMessage, TException, TClient>, IProvider<TContext, TToken, TMessage, TException, TClient>,
         IObservable<IProviderResult<TMessage>>
        where TContext : IProviderContext<TToken>
        where TClient : class
        where TException : ProviderException
    {
        public TToken Token { get; set; }
        public Provider() : base() { }
        public Provider(IFactory factory) : base(factory) { }
    }

    /// <summary>
    /// The Provider class is an observable container that functionalizes WCF Client requests and responses
    /// </summary>
    /// <typeparam name="TContext"></typeparam>
    /// <typeparam name="TMessage"></typeparam>
    /// <typeparam name="TException"></typeparam>
    /// <typeparam name="TClient"></typeparam>
     public class Provider<TContext, TMessage, TException, TClient> : IProvider<TContext , TMessage, TException, TClient>, IObservable<IProviderResult<TMessage>>
        where TContext : IProviderContext
        where TClient : class
        where TException : ProviderException
    {
         private List<IObserver<IProviderResult<TMessage>>> observers;
         private IFactory factory;

         public Provider() { observers = new List<IObserver<IProviderResult<TMessage>>>(); factory = new Factory(); }
         public Provider(IFactory factory) { observers = new List<IObserver<IProviderResult<TMessage>>>(); this.factory = factory; }

         public virtual void ExecuteServiceRequest<TResult>(
            TContext t,
            Func<TContext, IProviderResult<TResult, TMessage>> request,
            ClientBase<TClient> client)
         {
             this.ExecuteServiceRequest<TResult>(t, request, client, null);
         }
         public virtual void ExecuteServiceRequest<TResult>(
             TContext t,
             Func<TContext, IProviderResult<TResult, TMessage>> request,
             ClientBase<TClient> client,
             List<IEndpointBehavior> behaviors)
         {
             this.observers.ForEach(obv =>
             {
                 try
                 {
                     if (behaviors == null || behaviors.Count == 0)
                         this.AddDefaultEndpoint(obv, client);
                     else
                         this.AddServiceEndpointBehavior(behaviors, client);
                     client.Open();
                     obv.OnNext(request(t));
                 }
                 catch (FaultException fEx)
                 {
                     obv.OnError(factory.InvokeServiceException<TException>(fEx));
                 }
                 finally
                 {
                     client.Close();
                 }
             });
         }
         public virtual void ExecuteServiceRequest(
             TContext t,
             Func<TContext, IProviderResult<TMessage>> request,
             System.ServiceModel.ClientBase<TClient> client)
         {
             this.ExecuteServiceRequest(t, request, client, null);
         }
         public virtual void ExecuteServiceRequest(
             TContext t,
             Func<TContext, IProviderResult<TMessage>> request,
             ClientBase<TClient> client,
             List<IEndpointBehavior> behaviors)
         {
             this.observers.ForEach(obv =>
             {
                 try
                 {
                     if (behaviors == null || behaviors.Count == 0)
                         this.AddDefaultEndpoint(obv, client);
                     else
                         this.AddServiceEndpointBehavior(behaviors, client);
                     client.Open();
                     obv.OnNext(request(t));
                 }
                 catch (FaultException fEx)
                 {
                     obv.OnError(factory.InvokeServiceException<TException>(fEx));
                 }
                 finally
                 {
                     client.Close();
                 }
             });
         }
         private void AddDefaultEndpoint(IObserver<IProviderResult<TMessage>> obv, ClientBase<TClient> client)
         {
             if (obv is IProviderEventListener)
                 this.AddServiceEndpointBehavior(new List<IEndpointBehavior>() { new ProviderEndpointBehavior((IProviderEventListener)obv) }, client);
         }
         public virtual void AddServiceEndpointBehavior(
             List<IEndpointBehavior> behaviors,
             ClientBase<TClient> client)
         {
             behaviors.ForEach(behavior =>
             {
                 if (client != null)
                     client.Endpoint.Behaviors.Add(behavior);
             });
         }
         public virtual IDisposable Subscribe(IObserver<IProviderResult<TMessage>> observer)
         {
             if (!observers.Contains(observer))
                 observers.Add(observer);
             return new Unsubscriber(observers, observer);
         }
         public virtual void EndRequest()
         {
             foreach (var obv in observers.ToArray())
                 if (observers.Contains(obv))
                     obv.OnCompleted();
             observers.Clear();
         }

         #region Unsubscriber
         private class Unsubscriber : IDisposable
         {
             private List<IObserver<IProviderResult<TMessage>>> _observers;
             private IObserver<IProviderResult<TMessage>> _observer;

             public Unsubscriber(List<IObserver<IProviderResult<TMessage>>> observers, IObserver<IProviderResult<TMessage>> observer)
             {
                 this._observers = observers;
                 this._observer = observer;
             }

             public void Dispose()
             {
                 if (_observer != null && _observers.Contains(_observer))
                     _observers.Remove(_observer);
             }
         }
         #endregion
     }

     //public class Provider<TMessage, TException, TClient> : IProvider<TMessage, TException, TClient>, IObservable<IProviderResult<TMessage>>
     //    where TClient : class
     //    where TException : ProviderException
     //{
     //    private List<IObserver<IProviderResult<TMessage>>> observers;
     //    private IFactory factory;

     //    public Provider() { observers = new List<IObserver<IProviderResult<TMessage>>>(); factory = new Factory(); }
     //    public Provider(IFactory factory) { observers = new List<IObserver<IProviderResult<TMessage>>>(); this.factory = factory; }

     //    public virtual void ExecuteServiceRequest<TResult>(
     //       Func<IProviderResult<TResult, TMessage>> request,
     //       ClientBase<TClient> client)
     //    {
     //        this.ExecuteServiceRequest<TResult>(request, client, null);
     //    }
     //    public virtual void ExecuteServiceRequest<TResult>(
     //        Func<IProviderResult<TResult, TMessage>> request,
     //        ClientBase<TClient> client,
     //        List<IEndpointBehavior> behaviors)
     //    {
     //        this.observers.ForEach(obv =>
     //        {
     //            try
     //            {
     //                if (behaviors == null || behaviors.Count == 0)
     //                    this.AddDefaultEndpoint(obv, client);
     //                else
     //                    this.AddServiceEndpointBehavior(behaviors, client);
     //                client.Open();
     //                obv.OnNext(request);
     //            }
     //            catch (FaultException fEx)
     //            {
     //                obv.OnError(factory.InvokeServiceException<TException>(fEx));
     //            }
     //            finally
     //            {
     //                client.Close();
     //            }
     //        });
     //    }
     //    public virtual void ExecuteServiceRequest(
     //        Func<IProviderResult<TMessage>> request,
     //        System.ServiceModel.ClientBase<TClient> client)
     //    {
     //        this.ExecuteServiceRequest(request, client, null);
     //    }
     //    public virtual void ExecuteServiceRequest(
     //        Func<IProviderResult<TMessage>> request,
     //        ClientBase<TClient> client,
     //        List<IEndpointBehavior> behaviors)
     //    {
     //        this.observers.ForEach(obv =>
     //        {
     //            try
     //            {
     //                if (behaviors == null || behaviors.Count == 0)
     //                    this.AddDefaultEndpoint(obv, client);
     //                else
     //                    this.AddServiceEndpointBehavior(behaviors, client);
     //                client.Open();
     //                obv.OnNext(request);
     //            }
     //            catch (FaultException fEx)
     //            {
     //                obv.OnError(factory.InvokeServiceException<TException>(fEx));
     //            }
     //            finally
     //            {
     //                client.Close();
     //            }
     //        });
     //    }
     //    private void AddDefaultEndpoint(IObserver<IProviderResult<TMessage>> obv, ClientBase<TClient> client)
     //    {
     //        if (obv is IProviderEventListener)
     //            this.AddServiceEndpointBehavior(new List<IEndpointBehavior>() { new ProviderEndpointBehavior((IProviderEventListener)obv) }, client);
     //    }
     //    public virtual void AddServiceEndpointBehavior(
     //        List<IEndpointBehavior> behaviors,
     //        ClientBase<TClient> client)
     //    {
     //        behaviors.ForEach(behavior =>
     //        {
     //            if (client != null)
     //                client.Endpoint.Behaviors.Add(behavior);
     //        });
     //    }
     //    public virtual IDisposable Subscribe(IObserver<IProviderResult<TMessage>> observer)
     //    {
     //        if (!observers.Contains(observer))
     //            observers.Add(observer);
     //        return new Unsubscriber(observers, observer);
     //    }
     //    public virtual void EndRequest()
     //    {
     //        foreach (var obv in observers.ToArray())
     //            if (observers.Contains(obv))
     //                obv.OnCompleted();
     //        observers.Clear();
     //    }

     //    #region Unsubscriber
     //    private class Unsubscriber : IDisposable
     //    {
     //        private List<IObserver<IProviderResult<TMessage>>> _observers;
     //        private IObserver<IProviderResult<TMessage>> _observer;

     //        public Unsubscriber(List<IObserver<IProviderResult<TMessage>>> observers, IObserver<IProviderResult<TMessage>> observer)
     //        {
     //            this._observers = observers;
     //            this._observer = observer;
     //        }

     //        public void Dispose()
     //        {
     //            if (_observer != null && _observers.Contains(_observer))
     //                _observers.Remove(_observer);
     //        }
     //    }
     //    #endregion
     //}
}

